﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once

#include "ILevelType.h"
#include "DbDataSchema.h"
#include "ILevelHeaderTypeBehavior.h"

namespace gcmp
{
    //系统对象簇类型
    class GBMP_MODEL_EXPORT NewLevelType : public NdbObjectSchematic, public ILevelType
    {
        CANNOT_COPY_OR_ASSIGN(NewLevelType)
        DBOBJECT_DATA_BEGIN_AND_QUICK_CAST_FROM(gcmp, NewLevelType, gcmp::NdbObjectSchematic, 86DBC970 - 337A - 438C - 926D - D898BBCB9E1E, gbmpmodel, ILevelType)
            DATA_TRANSIENT(IElement*, pOwnerElement)
            DATA(Bool, IsStartHeaderVisible)
            DATA(Bool, IsEndHeaderVisible)
            MAP_DATA(std::wstring, OwnerPtr<ILevelHeaderTypeBehavior>, LevelHeaderTypeBehaviors)
            DATA(std::wstring, CurrentLevelHeaderTypeBehaviorName)
        DATA_END

    public:
        static ILevelType* Create(IDocument* pDoc, const std::wstring& name);
        static const ILevelType* GetLevelType(IDocument* pDoc, ElementId levelTypeId);
    public:
        virtual bool IsStartHeaderVisible() const override { return GetIsStartHeaderVisible__(); }
        virtual void SetIsStartHeaderVisible(bool visible) override { SetIsStartHeaderVisible__(visible); }
        virtual bool IsEndHeaderVisible() const override { return GetIsEndHeaderVisible__(); }
        virtual void SetIsEndHeaderVisible(bool visible) override { SetIsEndHeaderVisible__(visible); }

        virtual bool SetCurrentLevelHeaderTypeBehavior(const std::wstring& name) override;
        virtual const ILevelHeaderTypeBehavior* GetCurrentLevelHeaderTypeBehavior() const override;
        virtual const ILevelHeaderTypeBehavior* GetLevelHeaderTypeBehavior(const std::wstring& name) const override;
        virtual bool AddLevelHeaderTypeBehavior(OwnerPtr<ILevelHeaderTypeBehavior> opBehavior) override;
        virtual bool RemoveLevelHeaderTypeBehavior(const std::wstring& name) override;
        virtual std::vector<const ILevelHeaderTypeBehavior*> GetAllLevelHeaderTypeBehavior() const override;

        virtual bool SetOwnerElement(IElement* pOwnerElement) override
        {
            m_pOwnerElement = pOwnerElement;
            return true;
        }
        virtual IElement* GetOwnerElement() override { return m_pOwnerElement; }
        virtual const IElement* GetOwnerElement() const override { return m_pOwnerElement; }

#pragma region 关联更新

        /// \brief 当弱依赖的Element被删除后，此接口会被调用。
        /// \param deleteElementIds 表示将被删除的Element ids
        /// \return void
        virtual void UpdateForWeakParentDeletion(const std::set<ElementId>& deletedElementIds) override;

        /// \brief 报告依赖的ElementIds。对象删除和关联更新依赖于这个接口的正确实现。
        /// \param reporter 报告接口
        /// \return void
        virtual void ReportParents(IElementParentReporter& reporter) const override;

        /// \brief 外部用户报告自己定制的计算器
        ///
        /// GetCalculators中需要调用ICalculatorCollection::AddCalculator(OwnerPtr<ICalculator>)将计算器注册到ICalculatorCollection中。
        /// 若配合ADD_CALCULATOR宏使用，用户重写此方法时形参名也需使用“calculators”。
        /// \param calculators 计算器收集器指针
        /// \return void
        virtual void GetCalculators(ICalculatorCollection* calculators) const override {};
#pragma endregion 关联更新

#pragma region 参数

        /// \brief 报告原生参数
        /// \param parameterIds Element拥有的原生参数
        /// \return void
        virtual void ReportParameterDefinitions(std::vector<int>* pParamDefIds) const override;

        /// \brief 通过参数id来获得原生参数，此参数id文档唯一。
        /// \param parameterId 文档唯一的参数id
        /// \return Parameter 返回Parameter对象，如果失败则返回Parameter::InvalidParameter。
        virtual OwnerPtr<IParameter> GetNativeParameter(int paramDefId) const override;

        /// \brief 设置原生参数
        /// 如果Element有原生参数（Native Parameter），该类需定制SetNativeParameter方法。
        /// \param parameter 输入要设置的参数对象
        /// \param errorMessage 输出错误信息，为可选参数。失败的情况下获取错误信息。
        /// \return bool 返回true表示设置成功，否则表示设置失败。如果失败可以通过errorMessage参数获取错误信息。
        virtual bool SetNativeParameter(const IParameter *parameter, std::wstring* errorMsg = nullptr) override;

        /// \brief 获取参数是否可以被修改
        /// \param parameterId 文档唯一的参数id
        /// \return bool 如果参数可被修改，返回true。否则返回false。
        virtual bool IsParameterModifiable(int paramDefId) const override;
#pragma endregion 参数

#pragma region 复制粘贴
        /// \brief 报告引用的Element的id为拷贝、保持、放弃或者询问
        ///
        /// 假设Element A引用了Element B的id，只拷贝A得到A'，则当B
        /// 1. 报告为拷贝：拷贝B得到B'，A'中引用的B的Id替换成B'的Id。
        /// 2. 报告为保持：不拷贝B，A'中引用的B的ElementId不变。
        /// 3. 报告为放弃：不拷贝B，为放弃引用的A'中的B的id，将B的id作为参数，调用A'的UpdateForWeakParentDeletion方法。\param n1 参数1说明
        ///
        /// \param copyContext 拷贝上下文
        /// \param reporter 拷贝策略报告器
        /// \return void
        virtual void Report(const gcmp::ICopyContext & copyContext, gcmp::ICopyStrategyReporter& reporter) const override {};

        /// \brief 复制粘贴后更新操作
        ///
        /// 复制粘贴后需要的后处理可以放在这里，比如：对被复制粘贴后的元素进行重新命名
        ///
        /// \return void
        virtual void UpdateAfterCopy(const ICopyContext& copyContext) override {};

        /// \brief 当Weak Parent被拷贝但此Element没有同时被拷贝时，此函数被调用
        ///
        /// 调用时机：当Weak Parent被拷贝但此Element没有同时被拷贝时，此Element会通过此接口收到通知。 
        /// 用途：一般用来处理Element存在Id容器的情况。 
        /// 例子：当Sketch里的对象被拷贝但Sketch本身没被拷贝时，Sketch通过实现此接口将拷贝的新对象加到SketchObjectId容器中。 
        ///
        /// \param pDocumentFrom 源文档
        /// \param pSource 被拷贝的IElement指针
        /// \param pCopy 拷贝得到的IElement指针
        /// \return void
        virtual void UpdateForWeakParentCopied(
            const IDocument* pDocumentFrom,
            const IElement* pSource,
            const IElement* pCopy
        ) const override {};

        /// \brief 决定是否需要一起被拷贝
        ///
        /// 调用时机：当一个对象是否拷贝不仅仅取决于一个对象时，无法通过Report来直接决定是否拷贝。 
        /// 用途：根据待拷贝对象集来决定自己是否拷贝 
        /// 例子：墙连接取决于连接的两个墙、尺寸约束取决于约束的对象 
        ///
        /// \param copyContext 拷贝上下文
        /// \return bool 返回true表示需要，false表示不需要。
        virtual bool ShouldBeCopiedTogether(const ICopyContext & copyContext) const override { return false; };

        /// \brief 跨文档拷贝时判断StrongParents是否需要被拷贝
        ///
        /// 调用时机：跨文档拷贝时判断StrongParents是否需要被拷贝。 
        ///
        /// \return bool 返回true表示需要，false表示不需要。
        virtual bool IsStrongParentsNeedCopied(void) const override { return false; };

        /// \brief 判断是否可以复制
        /// \param pErrorMessage 输出信息
        /// \return bool 返回true表示可以复制，false表示不能复制。
        virtual bool CanBeCopied(std::wstring* pErrorMessage) const override { return false; };

#pragma endregion 复制粘贴
        virtual bool Transform(const Matrix4d& matrix) override { return true; }

    public: //实现NdbObject接口
        virtual NdbObject* GetTopOwnerObject() const override;

    private:
        bool UpdateLevelHeaderBehavior();
        IDocument* GetDocument() const;
    };

}

